﻿using AspNetCore.Tools.DataEncryption;
using System.Security.Cryptography;
using System.Text;

namespace System;

public static class StringExtensions
{
    /// <summary>
    /// Base64编码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string Base64Encrypt(this string input)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(input);
        return Convert.ToBase64String(bytes);
    }

    /// <summary>
    /// Base64解码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string Base64Decrypt(this string input)
    {
        byte[] bytes = Convert.FromBase64String(input);
        return Encoding.UTF8.GetString(bytes);
    }

    /// <summary>
    /// 使用 MD5 算法计算字符串的哈希，包含 32 个字符
    /// </summary>
    /// <remarks>
    /// 微软官方表示已发现 MD5 和 SHA-1 算法不安全，建议改用 SHA-2。 SHA-2 包括 SHA256、SHA384 和 SHA512。
    /// 参考说明：https://learn.microsoft.com/zh-cn/dotnet/standard/security/cryptographic-services#hash-values
    /// </remarks>
    /// <param name="plainText">明文</param>
    /// <param name="uppercase">是否输出大写</param>
    /// <param name="is16">是否输出 16 位</param>
    /// <returns>哈希字符串</returns>
    public static string MD5Encrypt(this string plainText, bool uppercase = false, bool is16 = false)
    {
        ArgumentException.ThrowIfNullOrEmpty(plainText);

        var bytes = Encoding.UTF8.GetBytes(plainText);
        return HashEncryption.MD5Encrypt(bytes, uppercase, is16);
    }

    /// <summary>
    /// 使用 SHA1 算法计算字符串的哈希，包含 40 个字符
    /// </summary>
    /// <remarks>
    /// 微软官方表示已发现 MD5 和 SHA-1 算法不安全，建议改用 SHA-2。 SHA-2 包括 SHA256、SHA384 和 SHA512。
    /// 参考说明：https://learn.microsoft.com/zh-cn/dotnet/standard/security/cryptographic-services#hash-values
    /// </remarks>
    /// <param name="plainText">明文</param>
    /// <param name="uppercase">是否输出大写</param>
    /// <returns>哈希字符串</returns>
    public static string SHA1Encrypt(this string plainText, bool uppercase = false)
    {
        ArgumentException.ThrowIfNullOrEmpty(plainText);

        var bytes = Encoding.UTF8.GetBytes(plainText);
        return HashEncryption.SHA1Encrypt(bytes, uppercase);
    }

    /// <summary>
    /// 使用 SHA256 算法计算字符串的哈希，包含 64 个字符
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="uppercase">是否输出大写</param>
    /// <returns>哈希字符串</returns>
    public static string SHA256Encrypt(this string plainText, bool uppercase = false)
    {
        ArgumentException.ThrowIfNullOrEmpty(plainText);

        var bytes = Encoding.UTF8.GetBytes(plainText);
        return HashEncryption.SHA256Encrypt(bytes, uppercase);
    }

    /// <summary>
    /// 使用 CRC-32 算法计算字符串的哈希
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="uppercase">是否输出大写</param>
    /// <returns>哈希字符串</returns>
    public static string Crc32Encrypt(this string plainText, bool uppercase = false)
    {
        ArgumentException.ThrowIfNullOrEmpty(plainText);

        var bytes = Encoding.UTF8.GetBytes(plainText);
        return HashEncryption.Crc32Encrypt(bytes, uppercase);
    }

    /// <summary>
    /// 使用对称加密算法 AES 类加密字符串
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="key">对称算法的密钥</param>
    /// <param name="iv">对称算法的初始化向量</param>
    /// <param name="mode">对称算法的运算模式</param>
    /// <param name="padding">对称算法中使用的填充模式</param>
    /// <returns>返回加密后的 Base64 格式字符串</returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static string AESEncrypt(this string plainText,
        string key,
        string? iv,
        CipherMode mode = CipherMode.CBC,
        PaddingMode padding = PaddingMode.PKCS7)
    {
        // Check arguments.
        if (string.IsNullOrWhiteSpace(plainText))
            throw new ArgumentNullException("plainText");

        using (var aes = Aes.Create())
        {
            if (!string.IsNullOrWhiteSpace(key))
                aes.Key = Encoding.UTF8.GetBytes(key);
            if (!string.IsNullOrWhiteSpace(iv))
                aes.IV = Encoding.UTF8.GetBytes(iv);

            aes.Mode = mode;
            aes.Padding = padding;

            var encryptor = aes.CreateEncryptor();
            using (var msEncrypt = new MemoryStream())
            {
                using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (var swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);
                    }

                    var encrypted = msEncrypt.ToArray();
                    return Convert.ToBase64String(encrypted);
                }
            }
        }
    }

    /// <summary>
    /// 使用对称加密算法 AES 类解密字符串
    /// </summary>
    /// <param name="cipherText">Base64 格式的密文</param>
    /// <param name="key">对称算法的密钥</param>
    /// <param name="iv">对称算法的初始化向量</param>
    /// <param name="mode">对称算法的运算模式</param>
    /// <param name="padding">对称算法中使用的填充模式</param>
    /// <returns>返回解密后的字符串</returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static string AESDecrypt(this string cipherText,
        string key,
        string? iv,
        CipherMode mode = CipherMode.CBC,
        PaddingMode padding = PaddingMode.PKCS7)
    {
        // Check arguments.
        if (string.IsNullOrWhiteSpace(cipherText))
            throw new ArgumentNullException("cipherText");

        string plainText;
        var cipherBytes = Convert.FromBase64String(cipherText);
        using (var aes = Aes.Create())
        {
            if (!string.IsNullOrWhiteSpace(key))
                aes.Key = Encoding.UTF8.GetBytes(key);
            if (!string.IsNullOrWhiteSpace(iv))
                aes.IV = Encoding.UTF8.GetBytes(iv);

            aes.Mode = mode;
            aes.Padding = padding;

            var encryptor = aes.CreateDecryptor();
            using (var msDecrypt = new MemoryStream(cipherBytes))
            {
                using (var csDecrypt = new CryptoStream(msDecrypt, encryptor, CryptoStreamMode.Read))
                {
                    using (var srDecrypt = new StreamReader(csDecrypt))
                    {
                        plainText = srDecrypt.ReadToEnd();
                    }
                }
            }
        }
        return plainText;
    }

    /// <summary>
    /// 使用对称加密算法国密 SM4 类加密字符串
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="key">对称算法的密钥</param>
    /// <param name="iv">对称算法的初始化向量</param>
    /// <param name="mode">对称算法的运算模式</param>
    /// <param name="padding">对称算法中是否使用填充模式</param>
    /// <returns>返回加密后的 Base64 格式字符串</returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static string SM4Encrypt(this string plainText,
        string key,
        string? iv,
        SM4CipherMode mode = SM4CipherMode.CBC,
        bool padding = true)
    {
        // Check arguments.
        if (string.IsNullOrWhiteSpace(plainText))
            throw new ArgumentNullException("plainText");

        var plainBytes = Encoding.UTF8.GetBytes(plainText);
        using (var sm4 = new SM4())
        {
            if (!string.IsNullOrWhiteSpace(key))
                sm4.Key = Encoding.UTF8.GetBytes(key);
            if (!string.IsNullOrWhiteSpace(iv))
                sm4.IV = Encoding.UTF8.GetBytes(iv);

            sm4.Mode = mode;
            sm4.Padding = padding;

            var cipherText = Convert.ToBase64String(sm4.Encrypt(plainBytes));
            return cipherText;
        }
    }

    /// <summary>
    /// 使用对称加密算法国密 SM4 解密字符串
    /// </summary>
    /// <param name="cipherText">Base64 格式的密文</param>
    /// <param name="key">对称算法的密钥</param>
    /// <param name="iv">对称算法的初始化向量</param>
    /// <param name="mode">对称算法的运算模式</param>
    /// <param name="padding">对称算法中是否使用填充模式</param>
    /// <returns>返回解密后的字符串</returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static string SM4Decrypt(this string cipherText,
        string key,
        string? iv,
        SM4CipherMode mode = SM4CipherMode.CBC,
        bool padding = true)
    {
        // Check arguments.
        if (string.IsNullOrWhiteSpace(cipherText))
            throw new ArgumentNullException("cipherText");

        var cipherBytes = Convert.FromBase64String(cipherText);
        using (var sm4 = new SM4())
        {
            if (!string.IsNullOrWhiteSpace(key))
                sm4.Key = Encoding.UTF8.GetBytes(key);
            if (!string.IsNullOrWhiteSpace(iv))
                sm4.IV = Encoding.UTF8.GetBytes(iv);

            sm4.Mode = mode;
            sm4.Padding = padding;

            var plainText = Encoding.UTF8.GetString(sm4.Decrypt(cipherBytes));
            return plainText;
        }
    }
}
